﻿using DocumentFormat.OpenXml.Lite.Core.Office;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IO = System.IO;
using System.Xml.Serialization;
using DocumentFormat.OpenXml.Lite.Shared;
using DocumentFormat.OpenXml.Lite.SpreadsheetML.Excel.Types;
using DocumentFormat.OpenXml.Lite.SpreadsheetML.Excel.WorkbookElements;
using DocumentFormat.OpenXml.Lite.SpreadsheetML.Excel.Shared;
using DocumentFormat.OpenXml.Lite.SpreadsheetML.Drawing;
using DFS = DocumentFormat.OpenXml.Lite.Core.DocumentFS;
using DocumentFormat.OpenXml.Lite.Core.Relationship;
using DocumentFormat.OpenXml.Lite.Core.Interfaces;

namespace DocumentFormat.OpenXml.Lite.SpreadsheetML.Excel.Base
{
    [XmlRoot("workbook", Namespace = Constants.ExcelMain)]
    public class Workbook : ISerializable
    {
        [XmlArray("sheets", Namespace = Constants.ExcelMain)]
        [XmlArrayItem("sheet", Namespace = Constants.ExcelMain)]
        public Sheet[] Sheets { get; set; }

        [XmlIgnore]
        public bool SheetsSpecific { get { return Sheets != null; } }

        [XmlIgnore]
        private SpreadsheetDocument manager;

        [XmlIgnore]
        public Guid Id { get { return manager.Id; } }

        [XmlIgnore]
        public AppProperties AppProperties
        {
            get { return manager.AppProperties; }
            set { manager.AppProperties = value; }
        }

        [XmlIgnore]
        public CoreProperties CoreProperties
        {
            get { return manager.CoreProperties; }
            set { manager.CoreProperties = value; }
        }

        [XmlNamespaceDeclarations]
        public XmlSerializerNamespaces Namespaces { get; set; }

        private Workbook()
        {
            Sheets = new Sheet[] { };

            Namespaces = new XmlSerializerNamespaces();
            Namespaces.Add("", Constants.ExcelMain);
            //ns.Add("xml", Constants.Xml);
        }

        internal Workbook(SpreadsheetDocument parent)
            : this()
        {
            this.manager = parent;
        }

        [XmlIgnore]
        public List<Worksheet> Worksheets { get; set; }

        public Worksheet CreateWorksheet(string name, Nullable<ST_SheetState> sheetState)
        {
            if (Worksheets == null)
            {
                Worksheets = new List<Worksheet>();
            }

            var ws = manager.CreateWorksheet(name, sheetState);
            Worksheets.Add(ws);
            return ws;

        }

        [XmlIgnore]
        public SharedStringTable SharedStringTable { get; set; }

        public SharedStringTable CreateSharedStringTable()
        {
            SharedStringTable =  manager.CreateSharedStringTable();
            return SharedStringTable;
        }

        [XmlIgnore]
        public StyleSheet StyleSheet { get; set; }

        public StyleSheet CreateStyleSheet()
        {
            StyleSheet = manager.CreateStyleSheet();
            return StyleSheet;
        }

        [XmlIgnore]
        public List<Table.Table> Tables { get; set; }

        public Table.Table CreateTableFor(Worksheet sheet, string displayName, string reference)
        {
            if (Tables == null)
            {
                Tables = new List<Table.Table>();
            }

            Table.Table t = manager.CreateTable(sheet);
            t.Reference = reference;
            t.DisplayName = displayName;

            Tables.Add(t); 
            return t;
        }

        internal void AddNamespace(string prefix, string ns)
        {
            if (!Namespaces.ToArray().Any(n => n.Name == prefix && n.Namespace == ns))
            {
                Namespaces.Add(prefix, ns);
            }
        }

        internal void AddSheet(string name, Nullable<ST_SheetState> sheetState, uint id, string relId)
        {
            Sheet s = new Sheet()
            {
                Id = id,
                Name = name,
                State = sheetState,
                RelationshipId = relId
            };
            Sheet[] temp = new Sheet[Sheets.Length + 1];
            Array.Copy(Sheets, temp, Sheets.Length);
            temp[Sheets.Length] = s;
            Sheets = temp;
        }

        public T FindElement<T>(string relationshipId)
        {
            return manager.FindElementFor<T>(this, relationshipId);
        }

        public T FindElementFor<T>(object obj, string relationshipId)
        {
            return manager.FindElementFor<T>(obj, relationshipId);
        }

        #region ISerializable members

        public byte[] Serialize()
        {
            XmlSerializer serializer = new XmlSerializer(this.GetType());
            using (var stream = new IO.MemoryStream())
            {
                using (var writer = new IO.StreamWriter(stream, Encoding.UTF8))
                {
                    serializer.Serialize(writer, this, Namespaces);
                    return stream.ToArray();
                }
            }
        }

        public void Deserialize(System.IO.Stream content)
        {
            XmlSerializer serializer = new XmlSerializer(this.GetType(), Constants.ExcelMain);
            var wb = serializer.Deserialize(content) as Workbook;

            this.Sheets = wb.Sheets;
            this.Namespaces = wb.Namespaces;
        }

        #endregion

        public void Save(string path)
        {
            DocumentManager.Instance.SaveDocument(manager.Id, path);
        }

        public void Save(System.IO.Stream stream)
        {
            DocumentManager.Instance.SaveDocument(manager.Id, stream);
        }
    }
}
